package com.hmall.transport.netty.service;

import com.hmall.factory.SingletonFactory;
import com.hmall.provider.Impl.ZkServiceProviderImpl;
import com.hmall.provider.ServiceProvider;
import com.hmall.remoting.constants.RpcServiceConfig;
import com.hmall.transport.netty.codec.RpcMessageDecoder;
import com.hmall.transport.netty.codec.RpcMessageEncoder;
import com.hmall.utils.RuntimeUtil;
import com.hmall.utils.ThreadPoolFactoryUtil;
import com.hmall.zookeeper.config.CustomShutdownHook;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component()
public class NettyRpcServer {

    public static final int PORT = 9998;
    private final ServiceProvider serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);
    public void registerService(RpcServiceConfig rpcServiceConfig) {
        serviceProvider.publishService(rpcServiceConfig);
    }

    public void start() {
        CustomShutdownHook.getCustomShutdownHook().clearAll();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
                RuntimeUtil.cpus() * 2,
                ThreadPoolFactoryUtil.createThreadFactory("service-handler-group", false)
        );
        try {
            String host = InetAddress.getLocalHost().getHostAddress();
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    // TCP默认开启了 Nagle 算法，该算法的作用是尽可能的发送大数据快，减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 是否开启 TCP 底层心跳机制
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // 当客户端第一次进行请求的时候才会进行初始化
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {
                            sc.pipeline().addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                            sc.pipeline().addLast(new RpcMessageEncoder());
                            sc.pipeline().addLast(new RpcMessageDecoder());
                            sc.pipeline().addLast(serviceHandlerGroup,new NettyRpcServerHandler());//费时操作交由serviceHandlerGroup线程异步完成
                        }
                    });
            ChannelFuture future = bootstrap.bind(host,PORT).sync();//同步等待绑定完成
            log.info("NettyRpcServer started at {}:{}", host, PORT);
            future.channel().closeFuture().sync();//等待服务端监听端口关闭
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
            serviceHandlerGroup.shutdownGracefully();
        }
    }
}
